﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Ltf.Remoting;
using Ltf.MonitorLizard.Helpers;
using Ltf.Remoting.Server;
using System.Diagnostics;


namespace Ltf.MonitorLizard
{
	public partial class LizardExplorer : Form
	{
		FileIcons treeFileIcons;
		FileIcons listFileIcons;
		ServerFunctions serverFunctions;

		FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();


		public LizardExplorer()
		{
			serverFunctions = new ServerFunctions();

			InitializeComponent();
			tvFileSystem.StateImageList = treeImages;
			tvFileSystem.ImageList = treeImages;
			lvFiles.SmallImageList = treeImages;
			treeFileIcons = new FileIcons(treeImages);


			fileSystemWatcher.Changed += new FileSystemEventHandler(fsw_Changed);
			fileSystemWatcher.Deleted += new FileSystemEventHandler(fsw_Changed);
			fileSystemWatcher.Created += new FileSystemEventHandler(fsw_Changed);
			fileSystemWatcher.Renamed += new RenamedEventHandler(fsw_Renamed);


			//ClearTree();
			//LoadDrives();

			NavigateToPath(@"C:\");
		}

		#region event handlers
		private void tvFileSystem_BeforeExpand(object sender, TreeViewCancelEventArgs e)
		{
			TreeNode node = e.Node;
			if (e.Action == TreeViewAction.Expand && node.Nodes.Count == 1
							&& node.Nodes[0].Text == "Loading...")
			{
				LoadFolders(node);
			}
		}
		private void tvFileSystem_AfterSelect(object sender, TreeViewEventArgs e)
		{
			if (e.Node == null || e.Node.Tag == null)
				return;
			string folder = e.Node.Tag.ToString();
			if (Directory.Exists(folder))
			{
				LoadFiles(folder);

			}
		}

		private void lvFiles_SelectedIndexChanged(object sender, EventArgs e)
		{

		}

		private void lvFiles_DoubleClick(object sender, EventArgs e)
		{
			if (lvFiles.SelectedItems.Count == 1)
			{
				string filePath = lvFiles.SelectedItems[0].Tag.ToString();
				tvFileSystem.SelectedNode.Expand();
				foreach (TreeNode tn in tvFileSystem.SelectedNode.Nodes)
				{
					if (tn.Tag.ToString() == filePath)
					{
						tvFileSystem.SelectedNode = tn;
					}
				}
			}
		}
		#endregion


		private void ClearTree()
		{
			tvFileSystem.Nodes.Clear();
		}

		private void LoadDrives()
		{
			TreeNode rootNode = new TreeNode("Local Drives");
			tvFileSystem.Nodes.Add(rootNode);

			foreach (DriveInfo driveInfo in DriveInfo.GetDrives())
			{
				if (driveInfo.IsReady)
				{
					TreeNode driveNode = new TreeNode(driveInfo.Name + (string.IsNullOrWhiteSpace(driveInfo.VolumeLabel) ? string.Empty : ("(" + driveInfo.VolumeLabel + ")")));
					rootNode.Nodes.Add(driveNode);
					driveNode.Tag = driveInfo.Name;
					driveNode.Nodes.Add("Loading...");
					driveNode.Name = driveInfo.Name;
					driveNode.ImageKey = treeFileIcons.LoadIconForFileName(driveInfo.Name);
					driveNode.SelectedImageKey = driveNode.ImageKey;
				}
			}

			rootNode.Expand();
		}

		private void LoadFolders(TreeNode node)
		{
			if (node.Nodes.Count == 1 && node.Nodes[0].Text == "Loading...")
			{
				node.Nodes.Remove(node.Nodes[0]);
			}

			foreach (string dir in Directory.GetDirectories(node.Tag.ToString()))
			{
				TreeNode dirNode = new TreeNode(Path.GetFileName(dir));
				node.Nodes.Add(dirNode);
				dirNode.Tag = dir;
				dirNode.Name = Path.GetFileName(dir);
				dirNode.Nodes.Add("Loading...");
				dirNode.ImageKey = treeFileIcons.LoadIconForFileName(System.Environment.SystemDirectory);
				dirNode.SelectedImageKey = dirNode.ImageKey;
			}

		}

		private void LoadFiles(string folder)
		{
			tstPath.Text = folder;
			fileSystemWatcher.EnableRaisingEvents = false;
			listImages.Images.Clear();
			listFileIcons = new FileIcons(listImages);
			lvFiles.Items.Clear();
			tsbWaiting.Visible = true;
			List<ListViewItem> items = new List<ListViewItem>();
			foreach (string subfolderPath in Directory.GetDirectories(folder))
			{
				string subfolderName = Path.GetFileName(subfolderPath);
				ListViewItem dirItem = new ListViewItem(subfolderName);
				items.Add(dirItem);
				dirItem.Name = subfolderPath;
				dirItem.Tag = subfolderPath;
				dirItem.ImageKey = treeFileIcons.LoadIconForFileName(System.Environment.SystemDirectory);
			}
			foreach (string filePath in Directory.GetFiles(folder))
			{
				string fileName = Path.GetFileName(filePath);
				ListViewItem dirItem = new ListViewItem(fileName);
				dirItem.Name = filePath;
				dirItem.Tag = filePath;
				dirItem.ImageKey = treeFileIcons.LoadIconForFileExtension(Path.GetExtension(filePath));
				items.Add(dirItem);
			}
			lvFiles.Items.AddRange(items.ToArray());
			imageGetter ig = new imageGetter(GetTFSStatusImages);
			FilesByOverLayTypes filesByOverLayTypes = new FilesByOverLayTypes(serverFunctions, folder);
			ig.BeginInvoke(folder, filesByOverLayTypes, null, null);

			fileSystemWatcher.Path = folder;
			fileSystemWatcher.IncludeSubdirectories = true;
			fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName
				| NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size;

			fileSystemWatcher.EnableRaisingEvents = true;
			
		}

		void fsw_Renamed(object sender, RenamedEventArgs e)
		{
			listUpdateRenameHandler lurh = new listUpdateRenameHandler(updateListRename);
			this.Invoke(lurh, e.OldFullPath, e.FullPath);

		}

		delegate void listUpdateRenameHandler(string oldFullPath, string newFullPath);
		public void updateListRename(string oldFullPath, string newFullPath)
		{
			string name = Path.GetFileName(newFullPath);
			if (lvFiles.Items.ContainsKey(oldFullPath))
			{

				ListViewItem lvi = lvFiles.Items[oldFullPath];
				lvi.Text = name;
				lvi.Name = newFullPath;
				lvi.Tag = newFullPath;
				lvi.ImageKey = treeFileIcons.LoadIconForFileExtension(Path.GetExtension(newFullPath));
				UpdateListItem(lvi);
			}
		}

		void fsw_Changed(object sender, FileSystemEventArgs e)
		{
			string file = e.FullPath;
			WatcherChangeTypes changeType = e.ChangeType;
			listUpdateHandler luh = new listUpdateHandler(updateList);
			this.Invoke(luh, changeType, file);

		}


		delegate void listUpdateHandler(WatcherChangeTypes changeType, string file);

		public void updateList(WatcherChangeTypes changeType, string file)
		{
			switch (changeType)
			{
				case WatcherChangeTypes.Changed:
					{
						serverFunctions.ParseSingleFile(file);
						if (lvFiles.Items.ContainsKey(file))
							UpdateListItem(lvFiles.Items[file]);
						break;
					}
				
				case WatcherChangeTypes.Deleted:
					{
						if (lvFiles.Items.ContainsKey(file))
							lvFiles.Items.RemoveByKey(file);
						break;
					}
				case WatcherChangeTypes.Created:
					{
						string name = Path.GetFileName(file);
						ListViewItem lvi = new ListViewItem(name);
						lvi.Name = file;
						lvi.ImageKey = treeFileIcons.LoadIconForFileExtension(Path.GetExtension(file));
						lvi.Tag = file;
						lvFiles.Items.Add(lvi);
						UpdateListItem(lvi);
						break;
					}
			}
		}

		private void UpdateListItem(ListViewItem lvi)
		{
			string file = lvi.Tag.ToString();

			LizardFileInfo lfi = ServerFunctions.GetFileInfoForFile(file);

			if (lfi == null)
				return;

			if (File.Exists(file))
			{
				lvi.ImageKey = treeFileIcons.LoadIconForFileExtensionAndLtfStatus(Path.GetExtension(file), lfi.OverlayType);
			}
			else if (Directory.Exists(file))
			{
				lvi.ImageKey = treeFileIcons.LoadIconForFileNameAndLtfStatus(System.Environment.SystemDirectory, lfi.OverlayType);
			}

			lvi.UseItemStyleForSubItems = false;
			while (lvi.SubItems.Count < 8)
				lvi.SubItems.Add(new ListViewItem.ListViewSubItem());




			lvi.SubItems[1].Text = lfi.ServerVersion.ToString();
			lvi.SubItems[2].ForeColor = (lfi.ServerVersion == lfi.LocalVersion) ? Color.DarkGreen : Color.DarkRed;
			lvi.SubItems[2].Text = lfi.LocalVersion.ToString();
			lvi.SubItems[3].Text = lfi.ChangeType;
			lvi.SubItems[4].Text = lfi.HasOtherPendingChange.ToString() == "false" ? "" : "Y";
			lvi.SubItems[5].Text = lfi.OtherUsers;
			lvi.SubItems[6].Text = lfi.LockStatus;
			lvi.SubItems[7].Text = lfi.LockOwner;
		}

		private delegate void imageGetter(string folder, FilesByOverLayTypes filesByOverLayTypes);
		private delegate void statusSetter(List<ListItemData> listItemDataList, ImageList imageList);

		private void GetTFSStatusImages(string folder, FilesByOverLayTypes filesByOverLayTypes)
		{
			ImageList iml = new ImageList();
			FileIcons icons = new FileIcons(iml);

			filesByOverLayTypes.LoadStatuses();

			List<ListItemData> listItemDataList = new List<ListItemData>();

			List<string> statusItems = new List<string>();

			foreach (KeyValuePair<string, LizardOverLayStatus> kvp in filesByOverLayTypes)
			{
				ListItemData listItemData = new ListItemData();
				string file = kvp.Key;
				LizardOverLayStatus status = kvp.Value;
				if (!string.IsNullOrEmpty(file))
				{
					if (file != (folder + "\\"))
					{
						listItemData.ListItemKey = file;
						if (File.Exists(file))
						{
							listItemData.ImageKey = icons.LoadIconForFileExtensionAndLtfStatus(Path.GetExtension(file), status);
						}
						else if (Directory.Exists(file))
						{
							listItemData.ImageKey = icons.LoadIconForFileNameAndLtfStatus(System.Environment.SystemDirectory, status);
						}

						LizardFileInfo lfi = ServerFunctions.GetFileInfoForFile(file);

                        if (lfi!=null)
						    FillListItemDataFromFileInfo(listItemData, lfi);

						listItemDataList.Add(listItemData);
						statusItems.Add(file);
					}
				}
			}

		
			statusSetter ss = new statusSetter(GotTFSStatusImages);

			this.Invoke(ss, listItemDataList, iml);
		}

		private static void FillListItemDataFromFileInfo(ListItemData listItemData, LizardFileInfo lfi)
		{
			listItemData.ServerVersion = lfi.ServerVersion.ToString();
			listItemData.ItemColor = (lfi.ServerVersion == lfi.LocalVersion) ? Color.DarkGreen : Color.DarkRed;
			listItemData.LocalVersion = lfi.LocalVersion.ToString();
			listItemData.ChangeType = lfi.ChangeType;
			listItemData.HasOtherPendingChange = lfi.HasOtherPendingChange.ToString() == "false" ? "" : "Y";
			listItemData.OtherUsers = lfi.OtherUsers;
			listItemData.LockStatus = lfi.LockStatus;
			listItemData.LockOwner = lfi.LockOwner;
		}

		private void GotTFSStatusImages(List<ListItemData> listItemDataList, ImageList imageList)
		{
			treeFileIcons.AddImageRange(imageList);

			foreach (ListItemData lid in listItemDataList)
			{
				if (lvFiles.Items.ContainsKey(lid.ListItemKey))
				{
					ListViewItem item = lvFiles.Items[lid.ListItemKey];
					item.ImageKey = lid.ImageKey;

					item.UseItemStyleForSubItems = false;
					while (item.SubItems.Count < 8)
						item.SubItems.Add(new ListViewItem.ListViewSubItem());

					item.SubItems[1].Text = lid.ServerVersion;
					item.SubItems[2].ForeColor = lid.ItemColor;
					item.SubItems[2].Text = lid.LocalVersion;
					item.SubItems[3].Text = lid.ChangeType;
					item.SubItems[4].Text = lid.HasOtherPendingChange;
					item.SubItems[5].Text = lid.OtherUsers;
					item.SubItems[6].Text = lid.LockStatus;
					item.SubItems[7].Text = lid.LockOwner;
				}
			}
			tsbWaiting.Visible = false;
		}

		private class ListItemData
		{
			public string ListItemKey { get; set; }
			public string ImageKey { get; set; }
			public string ServerVersion { get; set; }
			public string LocalVersion { get; set; }
			public Color ItemColor { get; set; }
			public string ChangeType { get; set; }
			public string HasOtherPendingChange { get; set; }
			public string OtherUsers { get; set; }
			public string LockStatus { get; set; }
			public string LockOwner { get; set; }

		}

		private class FilesByOverLayTypes : Dictionary<string, LizardOverLayStatus>
		{
			private ServerFunctions serverFunctions;
			private string folderPath;

			public FilesByOverLayTypes(ServerFunctions serverFunctions, string folderPath)
			{
				this.serverFunctions = serverFunctions;
				this.folderPath = folderPath;

			}

			public void LoadStatuses()
			{
				foreach (string lfsName in Enum.GetNames(typeof(LizardOverLayStatus)))
				{
					string[] files = serverFunctions.GetFilesForOverlayType(folderPath, lfsName).Split('|');
					foreach (string file in files)
					{
						string filePath = string.Format("{0}\\{1}", folderPath, file);
						this[filePath] = (LizardOverLayStatus)Enum.Parse(typeof(LizardOverLayStatus), lfsName);
					}
				}
			}
		}

		private void lvFiles_Click(object sender, EventArgs e)
		{
			if (listClickButtons == MouseButtons.Right)
			{
				StringBuilder sb = new StringBuilder();
				foreach (ListViewItem lvi in lvFiles.SelectedItems)
					sb.Append(string.Format("\"{0}\" ", lvi.Tag.ToString()));

				string filesList = sb.ToString();
				ShowContextMenu(filesList);
			}
		}

		private void ShowContextMenu(string filesList)
		{
			Dictionary<string, ContextMenuAttribute> menus = serverFunctions.GetMenus(filesList);

			cmsLizard.Items.Clear();

			AddContextMenuItem(cmsLizard, filesList, "Open");
			AddContextMenuItem(cmsLizard, filesList, "Open With...");
			cmsLizard.Items.Add(new ToolStripSeparator());
			AddContextMenuItem(cmsLizard, filesList, "Copy");
			AddContextMenuItem(cmsLizard, filesList, "Paste");
			cmsLizard.Items.Add(new ToolStripSeparator());

			foreach (KeyValuePair<string, ContextMenuAttribute> kvp in menus)
			{
				if (kvp.Value.Text.StartsWith("--"))
				{
					ToolStripSeparator tss = new ToolStripSeparator();
					cmsLizard.Items.Add(tss);
				}
				else
				{
					ToolStripMenuItem tsi = new ToolStripMenuItem(kvp.Value.Text);
					tsi.Enabled = !kvp.Value.Disabled;
					tsi.Tag = filesList;
					cmsLizard.Items.Add(tsi);
					tsi.Click += new EventHandler(tsi_Click);
				}
			}

			cmsLizard.Show(MousePosition);
		}

		void AddContextMenuItem(ContextMenuStrip cms, string fileList, string text)
		{
			ToolStripMenuItem tsi = new ToolStripMenuItem(text);
			tsi.Tag = fileList;
			cmsLizard.Items.Add(tsi);
			tsi.Click += new EventHandler(tsi_Click);
		}

		void tsi_Click(object sender, EventArgs e)
		{
			ToolStripItem tsi = sender as ToolStripItem;

			if (cmsLizard.Items.IndexOf(tsi) < 5)
			{
				ProcessLocalAction(tsi);
			}
		            // ordinal is -5 to take into account the open o/w copy and cut commands
			serverFunctions.RunMenuCommand(cmsLizard.Items.IndexOf(tsi) - 5 , tsi.Tag.ToString(), ContextMenuUsage.ExplorerOnly);

			
		}

		private void ProcessLocalAction(ToolStripItem tsi)
		{
			string[] fileList = ServerFunctions.GetPaths(tsi.Tag.ToString());

			if (tsi.Text == "Open")
			{
				Process p = new Process();
				ProcessStartInfo psi = new ProcessStartInfo();
				psi.UseShellExecute = true;
				psi.WindowStyle = ProcessWindowStyle.Hidden;
				psi.FileName = fileList[0];
				p.StartInfo = psi;

				p.Start();
				return;
			}

			if (tsi.Text == "Open With...")
			{
				Process p = new Process();
				ProcessStartInfo psi = new ProcessStartInfo();
				psi.UseShellExecute = true;
				psi.WindowStyle = ProcessWindowStyle.Hidden;
				psi.FileName = "rundll32.exe";
				psi.Arguments = "shell32.dll,OpenAs_RunDLL " + fileList[0];
				p.StartInfo = psi;

				p.Start();
				return;
			}


			if (tsi.Text == "Copy")
			{
				Clipboard.SetData(System.Windows.Forms.DataFormats.FileDrop, fileList);
				return;
			}

			if (tsi.Text == "Paste")
			{
				object obj = Clipboard.GetData(System.Windows.Forms.DataFormats.FileDrop);
				if (obj is string[])
				{
					string[] files = obj as string[];
					foreach (string file in files)
					{
						File.Copy(file, Path.GetDirectoryName(fileList[0]) + "\\" + Path.GetFileName(file));
					}
				}
				return;
			}

		}

		MouseButtons listClickButtons = System.Windows.Forms.MouseButtons.None;

		private void lvFiles_MouseDown(object sender, MouseEventArgs e)
		{
			listClickButtons = e.Button;
		}




		MouseButtons treeClickButtons = System.Windows.Forms.MouseButtons.None;

		private void tvFileSystem_MouseDown(object sender, MouseEventArgs e)
		{
			treeClickButtons = e.Button;
		}

		private void tvFileSystem_Click(object sender, EventArgs e)
		{
			TreeNode tn = tvFileSystem.HitTest(tvFileSystem.PointToClient(MousePosition)).Node;

			if (tn == null || tn.Tag == null)
				return;
			string folder = tn.Tag.ToString();
			if (treeClickButtons == MouseButtons.Right)
			{
				ShowContextMenu(folder);
			}


		}

		private void toolStrip1_Resize(object sender, EventArgs e)
		{
			tstPath.Size = new Size(toolStrip1.Width - 230,25);
		}

		private void NavigateToPath(string fullPath)
		{
			ClearTree();
			LoadDrives();

			string path = fullPath;

			string drive = Path.GetPathRoot(path);
			if (tvFileSystem.Nodes[0].Nodes.ContainsKey(drive))
			{
				TreeNode node = tvFileSystem.Nodes[0].Nodes[drive];
				tvFileSystem.SelectedNode = node;
				tvFileSystem_BeforeExpand(null, new TreeViewCancelEventArgs(node,false, TreeViewAction.Expand));
				node.Expand();
				

				path = path.Substring(drive.Length);
				while (path != string.Empty)
				{
					string folder = path.Split(Path.DirectorySeparatorChar)[0];
					if (node.Nodes.ContainsKey(folder))
					{
						node = node.Nodes[folder];
						tvFileSystem.SelectedNode = node;
						tvFileSystem_BeforeExpand(null, new TreeViewCancelEventArgs(node, false, TreeViewAction.Expand));
						node.Expand();
					}
					if (path.Contains(Path.DirectorySeparatorChar.ToString()))
						path = path.Substring(folder.Length + 1);
					else
						path = string.Empty;
				}

				LoadFiles(fullPath);
			}

		}

		private void tsbRefresh_Click(object sender, EventArgs e)
		{
			RefreshTreeAndList();
		}

		private void RefreshTreeAndList()
		{
			NavigateToPath(tstPath.Text);
		}
	}
}
